<template>
  <view
    class="mask mxp-popup"
    :style="[data.maskStyle, { height: newPageHeight, opacity, transform: `translateY(${data.showPopup ? '0px' : newPageHeight})` }]"
    @click="hideClick"
  >
    <view class="content-view" :style="[data.contentStyle, amatons]" @click.stop.prevent="preventClick">
      <slot name="header">
        <mxp-toolbar
          v-if="typeof data.showToolbar === 'boolean' ? data.showToolbar : model === 'bottom' ? true : false"
          v-bind="toolbarOptions"
          @cancel="() => hide('button')"
          @confirm="onConfirm"
        ></mxp-toolbar>
      </slot>
      <view v-if="data.showContent" class="popup-vessel">
        <mxp-safe-status-bar v-if="safeAreaInsetTop && model !== 'bottom' && model !== 'center'"></mxp-safe-status-bar>
        <slot></slot>
        <mxp-safe-bottom v-if="safeAreaInsetBottom && model !== 'top' && model !== 'center'"></mxp-safe-bottom>
      </view>
    </view>
  </view>
</template>

<script lang="ts" setup>
  import { type CSSProperties, computed, nextTick, onMounted, ref, shallowReactive, watch } from 'vue';
  import type { $AllColorStr, $AllSizeUnitStr, $AnimationMode, $SizeUnitStr } from '@/mxp-ui/mxp-tool/types/interface';
  import { getAmatons } from '../mxp-tool/utils/dom';
  import MxpToolbar, { type $ToolbarProps } from '../mxp-toolbar/mxp-toolbar.vue';
  import MxpSafeBottom from '../mxp-safe-bottom/mxp-safe-bottom.vue';
  import MxpSafeStatusBar from '../mxp-safe-status-bar/mxp-safe-status-bar.vue';
  import { defThemeColor } from '../mxp-tool/theme/theme';
  import mxpGetSysInfoStore from '../mxp-tool/store/mxp-store-sys-info';

  export type $PopupModel = 'bottom' | 'center' | 'left' | 'right' | 'top';

  export type $PopupProps = {
    /** 是否显示工具条 (仅当 model === 'top' 时默认显示,其他默认不显示) */
    showToolbar?: boolean;
    /** 工具条的配置  */
    toolbarOptions?: $ToolbarProps;
    /** 圆角 可以是 Boolean 或者具体的圆角值如：10px */
    borderRadius?: $SizeUnitStr | boolean | (object & string);
    /** 弹出框显示状态 */
    modelValue?: boolean;
    /** 是否在动画执行完成后才显示内容 默认立即显示无需等待动画完成 */
    animationEndShowContent?: boolean;
    /** 左边偏移量 */
    offsetLeft?: $SizeUnitStr;
    /** 顶部偏移量 (如自定义导航栏等) */
    offsetTop?: $SizeUnitStr;
    /** 右边偏移量 */
    offsetRight?: $SizeUnitStr;
    /** 底部偏移量 (如自定义tab栏) */
    offsetBottom?: $SizeUnitStr;
    /** 背景色 */
    background?: $AllColorStr;
    /** 遮罩的背景色 */
    maskBackground?: $AllColorStr;
    /**
     * 方向
     * @param bottom - 底部弹出
     * @param top - 顶部弹出
     * @param left - 左边弹出
     * @param right - 右边弹出
     * @param center - 居中弹出
     */
    model?: 'bottom' | 'center' | 'left' | 'right' | 'top';
    /** 是否可以点击遮罩关闭 */
    maskClose?: boolean;
    /** 高度 model为：left,bottom,center时有效 */
    height?: $AllSizeUnitStr;
    /** 页面高度 */
    pageHeight?: $AllSizeUnitStr;
    /** 宽度 model为：top,right,center时有效,且当model */
    width?: $AllSizeUnitStr;
    /** 页面宽度 */
    pageWidth?: $AllSizeUnitStr;
    /** zIndex层级默认为 100 */
    zIndex?: number;
    /** 是否留出顶部安全区(状态栏高度) */
    safeAreaInsetTop?: boolean;
    /** 是否为留出底部安全区 (如 ios 底部黑色触控条) */
    safeAreaInsetBottom?: boolean;
    /** 动画持续时间 */
    duration?: number;
  };
  export type $PopupOptions = {
    /** 点击确定按钮时触发的回调 */
    confirm?: () => void;
    /** 动画完成时触发的回调 */
    animationEnd?: (type: 'hide-animation' | 'show-animation') => void;
  };

  defineOptions({
    name: 'mxp-popup',
  });
  const props = withDefaults(defineProps<$PopupProps>(), {
    /** 是否显示工具条 */
    showToolbar: undefined,
    /** 工具条的配置  */
    toolbarOptions: () => ({}),
    /** 圆角 可以是 Boolean 或者具体的圆角值如：10px */
    borderRadius: '10px',
    /** 弹出框显示状态 */
    modelValue: false,
    /** 是否在动画执行完成后才显示内容 默认立即显示无需等待动画完成 */
    animationEndShowContent: true,
    /** 左边偏移量 */
    offsetLeft: '0px',
    /** 顶部偏移量 (如自定义导航栏等) */
    offsetTop: '0px',
    /** 右边偏移量 */
    offsetRight: '0px',
    /** 底部偏移量 (如自定义tab栏) */
    offsetBottom: '0px',
    /** 背景色 */
    background: undefined,
    /** 遮罩的背景色 */
    maskBackground: 'rgba(0,0,0,0.3)',
    /**.
     * 方向
     * @param bottom - 底部弹出
     * @param top - 顶部弹出
     * @param left - 左边弹出
     * @param right - 右边弹出
     * @param center - 居中弹出
     */
    model: 'bottom',
    /** 是否可以点击遮罩关闭 */
    maskClose: true,
    /** 高度 model为：left,bottom,center时有效 */
    height: undefined,
    /** 页面高度 */
    pageHeight: undefined,
    /** 宽度 model为：top,right,center时有效 */
    width: undefined,
    /** 页面宽度 */
    pageWidth: '100vw',
    /** zIndex层级默认为 100 */
    zIndex: 100,
    /** 是否留出顶部安全区(状态栏高度) */
    safeAreaInsetTop: true,
    /** 是否为留出底部安全区 (如 ios 底部黑色触控条) */
    safeAreaInsetBottom: true,
    /** 动画持续时间 */
    duration: 300,
  });
  defineSlots<{
    /** 弹出框头部的内容 */
    header: () => void;
    /** 弹出框主体内容 */
    default: () => void;
    /** 自定义在弹出框外的内容 */
    root: () => void;
  }>();
  const emits = defineEmits<{
    (e: 'show'): void;
    (e: 'hide', type: 'button' | 'confirm' | 'mask' | 'rest'): void;
    (e: 'animationEnd', type: 'hide-animation' | 'show-animation'): void;
    (e: 'update:modelValue', type: boolean): void;
    (e: 'confirm'): void;
  }>();

  const opacity = ref(0);
  const sys = mxpGetSysInfoStore().getSysInfo();
  const newPageHeight = computed(() => props.pageHeight || `${sys.value.windowHeight}px`);
  const animationTimeout = ref<NodeJS.Timeout | undefined>(undefined);
  /** 对应关系字典 */
  const modelDic: Record<$PopupModel, $AnimationMode> = {
    bottom: 'slide-bottom',
    top: 'slide-top',
    left: 'slide-left',
    right: 'slide-right',
    center: 'zoom-in',
  };
  const data = shallowReactive({
    showToolbar: props.showToolbar ?? undefined,
    confirm: undefined as (() => void) | undefined,
    animationEnd: undefined as ((type: 'hide-animation' | 'show-animation') => void) | undefined,
    duration: props.duration,
    translateModel: props.model && props.model in modelDic ? modelDic[props.model as keyof typeof modelDic] : 'slide-bottom',
    showPopup: false,
    /** 是否显示内容 */
    showContent: !props.animationEndShowContent,
    closePosition: undefined,
    contentStyle: getContentStyle(),
    maskStyle: getMaskStyle(),
    mxpPopupStyle: {} as CSSProperties,
    closeViewStyle: {} as CSSProperties,
  });
  const amatons = ref<CSSProperties>(getAmatons(data.translateModel, false));

  watch(
    () => props.modelValue,
    (val) => {
      nextTick(() => {
        val ? show() : hide();
      });
    }
  );

  watch(
    () => props.height,
    () => {
      if (data.showPopup) {
        data.contentStyle = getContentStyle();
      }
    }
  );

  onMounted(() => {
    props.modelValue && show();
  });

  /**
   * 显示
   */
  function show(param: $PopupOptions = {}) {
    data.confirm = param.confirm;
    data.animationEnd = param.animationEnd;
    data.showContent = !props.animationEndShowContent;
    data.contentStyle = getContentStyle();
    data.maskStyle = getMaskStyle();
    nextTick(() => {
      data.showPopup = true;
      emits('update:modelValue', true);
      amatons.value = getAmatons(data.translateModel, false);
      emits('show');
      setTimeout(() => {
        nextTick(() => {
          amatons.value = getAmatons(data.translateModel, true);
          opacity.value = 1;
          animationTimeout.value = setTimeout(() => {
            data.showContent = true;
            emits('animationEnd', 'show-animation');
          }, data.duration - 10);
        });
      }, 10);
    });
  }

  /**
   * 关闭
   */
  function hide(type: 'button' | 'confirm' | 'mask' | 'rest' = 'rest') {
    emits('hide', type);
    emits('update:modelValue', false);
    opacity.value = 0;
    amatons.value = getAmatons(data.translateModel, false);
    clearTimeout(animationTimeout.value);
    animationTimeout.value = setTimeout(() => {
      animationTimeout.value = undefined;
      data.showPopup = false;
      emits('animationEnd', 'hide-animation');
    }, data.duration);
  }

  /**
   * 点击遮罩关闭
   */
  function hideClick() {
    if (props.maskClose) {
      hide('mask');
    }
  }

  /**
   * 提交
   */
  function onConfirm() {
    emits('confirm');
    typeof data.confirm === 'function' && data.confirm();
  }

  /**
   * 阻止传递
   */
  function preventClick() {
    // 阻止传递
  }

  /**
   * 得到弹出框动内容画的初始样式
   */
  function getContentStyle() {
    const safetyWidth = `calc(${props.pageWidth} - ${props.offsetLeft} - ${props.offsetRight})`;
    const safetyHeight = `calc(${newPageHeight.value} - ${props.offsetTop} - ${props.offsetBottom})`;
    const obj: CSSProperties = { transitionDuration: `${props.duration}ms`, background: props.background || defThemeColor.value.fill };
    let borderRadius = '10px';
    if (typeof props.borderRadius === 'boolean') {
      borderRadius = props.borderRadius ? '10px' : '0px';
    } else if (typeof props.borderRadius === 'string') {
      borderRadius = props.borderRadius;
    }

    if (props.model === 'bottom') {
      obj.width = safetyWidth;
      obj.height = props.height;
      obj.borderRadius = `${borderRadius} ${borderRadius} 0 0`;
      obj.bottom = '0px';
      obj.left = '0px';
    } else if (props.model === 'top') {
      obj.width = safetyWidth;
      obj.height = props.height;
      obj.borderRadius = `0 0 ${borderRadius} ${borderRadius}`;
      obj.top = '0px';
      obj.left = '0px';
    } else if (props.model === 'left') {
      obj.width = props.width;
      obj.height = safetyHeight;
      obj.borderRadius = `0 ${borderRadius} ${borderRadius} 0`;
      obj.top = '0px';
      obj.left = '0px';
    } else if (props.model === 'right') {
      obj.width = props.width;
      obj.height = safetyHeight;
      obj.borderRadius = `${borderRadius} 0 0 ${borderRadius}`;
      obj.top = '0px';
      obj.right = '0px';
    } else {
      obj.borderRadius = borderRadius;
      obj.width = props.width || undefined;
      obj.height = props.height || undefined;
    }
    if (props.model !== 'center') {
      obj.position = 'absolute';
    }
    return obj;
  }

  /** 得到遮罩的样式 */
  function getMaskStyle(): CSSProperties {
    return {
      zIndex: props.zIndex,
      top: props.offsetTop,
      left: props.offsetLeft,
      right: props.offsetRight,
      bottom: props.offsetBottom,
      transitionDuration: `${props.duration}ms`,
      backgroundColor: props.maskBackground,
    } satisfies CSSProperties;
  }

  defineExpose({
    /** 显示弹出框 */
    show,

    /** 关闭弹出框 */
    hide,
  });
</script>

<style lang="scss" scoped>
  .mxp-popup {
    position: fixed;
    top: 0;
    left: 0;
    display: flex;
    flex-flow: row nowrap;
    align-items: stretch;
    justify-content: center;
    width: 100%;
    height: 100vh;
    overflow: hidden;

    &.mask {
      display: flex;
      flex: 1;
      flex-flow: row nowrap;
      align-items: center;
      justify-content: center;
      opacity: 0;
      transition-timing-function: ease-in-out;
      transition-duration: 0.3s;
      transition-property: opacity;
      transform: translateY(100vh);
    }

    .content-view {
      transition-timing-function: ease-in-out;
      transition-duration: 0.3s;
      transition-property: transform, opacity;
    }

    .title-view {
      position: relative;
      display: flex;
      flex-flow: row nowrap;
      align-items: center;
      justify-content: center;
      height: 44px;
    }

    .popup-vessel {
      display: flex;
      flex-flow: column nowrap;
      align-items: center;
      justify-content: center;
    }

    .close-view {
      position: absolute;
      z-index: 2;
      display: flex;
      flex-flow: row nowrap;
      align-items: center;
      justify-content: center;
      height: 44px;
    }
  }
</style>
